#include "arbreArithmetique.h"

void initArbreVide(Arbre **arbre)
{
  if((*arbre) != NULL)
    {
      detruireArbre(arbre);
    }
  (*arbre) = (Arbre *) malloc(sizeof(Arbre));
  (*arbre)->element = NULL;
  (*arbre)->ensemble = creer_Ens();
}

void initArbreEntier(Arbre **arbre, const int i)
{
  if((*arbre) != NULL)
    {
      detruireArbre(arbre);
    }
  (*arbre) = (Arbre *) malloc(sizeof(Arbre));
  (*arbre)->element = creerEntier(&i);
  (*arbre)->ensemble = creer_Ens();
}

void initArbreChar(Arbre **arbre, const char *i)
{
  if((*arbre) != NULL)
    {
      detruireArbre(arbre);
    }
  (*arbre) = (Arbre *) malloc(sizeof(Arbre));
  if((*arbre) == NULL)
    {
      fprintf(stderr, "Erreur malloc\n");
      exit(1);
    }
  (*arbre)->element = creerChar(i);
  (*arbre)->ensemble = creer_Ens();
}

void initArbreCopie(Arbre **arbreDest, const Arbre *arbreSource)
{
  if(arbreSource == NULL)
    return;
  initArbreChar(arbreDest, arbreSource->element);
  prems_Ens(arbreSource->ensemble);
  while(arbreSource->ensemble->courant != NULL)
    {
      Arbre *arbre = NULL;
      initArbreCopie(&arbre, 
		     getData_Ens(arbreSource->ensemble));
      insert_Ens((*arbreDest)->ensemble, 
		 arbre,
		 NULL);
      suivant_Ens(arbreSource->ensemble);
    }
}

void initArbreTableau(Arbre **arbre, TableauDynamique *t)
{
  int i = 0;
  Arbre *arb1 = NULL;
  Arbre *arb2 = NULL;
  Arbre *arb3 = NULL;
  char valeur = 1;
  Pile p;
  initialiserPile(&p);
  for(i = 0; i < t->utilise; i++)
    {
      char *el = t->tableau[i];
      if(estOperateur(&el))
	{
	  if(testPileVide(&p) == 0)
	    {
	      initArbreChar(&arb1, sommetDeLaPile(&p));
	      dePiler(&p, detruireChar);
	    }
	  if(arb3 == NULL)
	    {
	      initArbreChar(&arb2, sommetDeLaPile(&p));
	      dePiler(&p, detruireChar);
	    }
	  else
	    {
	      arb2 = arb3;
	      arb3 = NULL;
	    }
	  valeur = 0;
	}
      switch((!estOperateur(&el) ? 1 : el[0]))
	{
	case '+': 
	  additionArbres(arb1, arb2, &arb3);
	  break;
	case '-':
	  soustractionArbres(arb2, arb1, &arb3);
	  break;
	case '*':
	  multiplicationArbres(arb1, arb2, &arb3);
	  break;
	case '/':
	  divisionArbre(arb2, arb1, &arb3);
	  break;
	default:
	  emPiler(&p, el, creerChar);
	  valeur = 1;
	  break;
	}
      if(!valeur)
	{
	  detruireArbre(&arb1);
	  detruireArbre(&arb2);
	}
    }
  (*arbre) = arb3;
  testamentPile(&p, detruireChar);
}

void detruireArbre(Arbre **arbre)
{
  if((*arbre) == NULL)
    return;
  prems_Ens((*arbre)->ensemble);
  while((*arbre)->ensemble->courant != NULL)
  {
      Arbre *arb = getData_Ens((*arbre)->ensemble);
      detruireArbre(&arb);
      suivant_Ens((*arbre)->ensemble);
  }
  detruire_Ens(&((*arbre)->ensemble), NULL);
  detruireChar(&((*arbre)->element));
  free((*arbre));
  (*arbre) = NULL;
}

void operationArbres(const Arbre* arbre1, const Arbre* arbre2, const char* op, Arbre **result)
{
  Arbre *c1 = NULL;
  Arbre *c2 = NULL;
  Arbre* tmp = NULL;
  initArbreChar(result, op);
  if((estOperateur((char **)&(arbre1->element)) 
      && (estOperateur((char **)&(arbre2->element))) 
      && (strcmp(arbre1->element,arbre2->element) == 0)
      && (strcmp((*result)->element,arbre2->element) == 0)))
  {
    prems_Ens(arbre1->ensemble);
    do
      {
	initArbreCopie(&tmp,getData_Ens(arbre1->ensemble));
	insert_Ens((*result)->ensemble,tmp,NULL);
	suivant_Ens(arbre1->ensemble);
	tmp = NULL;
      } while(arbre1->ensemble->courant != NULL);
	  	 
    prems_Ens(arbre2->ensemble);
    do
      {
	initArbreCopie(&tmp,getData_Ens(arbre2->ensemble));
	insert_Ens((*result)->ensemble,tmp,NULL);
	suivant_Ens(arbre2->ensemble);
	tmp = NULL;
      } while(arbre2->ensemble->courant != NULL);
	  
	  
  }
  else
  {
    initArbreCopie(&c1,arbre1);
    insert_Ens((*result)->ensemble, c1, NULL);
    initArbreCopie(&c2,arbre2);
    insert_Ens((*result)->ensemble, c2, NULL);
  }
}

void additionArbres(const Arbre* arbre1, const Arbre* arbre2, Arbre **result)
{
  operationArbres(arbre1, arbre2, "+", result);
}

void multiplicationArbres(const Arbre* arbre1, const Arbre* arbre2, Arbre** result)
{
  operationArbres(arbre1,arbre2,"*", result);
}

void soustractionArbres(const Arbre* arbre1, const Arbre* arbre2, Arbre **result)
{
  operationArbres(arbre1,arbre2,"-", result);
}

void divisionArbre(const Arbre* arbre1, const Arbre* arbre2, Arbre** result)
{
  operationArbres(arbre1,arbre2,"/", result);
}

int valeurArbre(const Arbre *arbre)
{
  int result = 0;
  char *op;
  if(!estOperateur((char **)&(arbre->element)))
    {
      if(getEntier((char **)&(arbre->element), &result) != 1)
	{
	  fprintf(stderr, "Erreur getEntier");  
	}
      return result;
    }
  prems_Ens(arbre->ensemble);
  op = arbre->element;
  result = valeurArbre(getData_Ens(arbre->ensemble));
  suivant_Ens(arbre->ensemble);
  while(arbre->ensemble->courant != NULL)
    {
      
      switch(op[0])
	{
	case '+':
	  result += valeurArbre(getData_Ens(arbre->ensemble));
	  break;
	case '*':
	  result *= valeurArbre(getData_Ens(arbre->ensemble));
	  break;
	case '-':
	  result -= valeurArbre(getData_Ens(arbre->ensemble));
	  break;
	case '/':
	  if(valeurArbre(getData_Ens(arbre->ensemble)) != 0)
	    result /= valeurArbre(getData_Ens(arbre->ensemble));
	  break;
	default:
	  fprintf(stderr, "Erreur dans l'opérande");
	  break;
	}
      suivant_Ens(arbre->ensemble);
    }
  return result;
}

void afficheArbreExpression(const Arbre *arbre)
{
  prems_Ens(arbre->ensemble);

  while(arbre->ensemble->courant != NULL)
    {
      Arbre *arb = getData_Ens(arbre->ensemble);
      char *element = arb->element;
      if(!estOperateur(&(element)))
	printf("%s ", element);
      if(estOperateur(&(element)))
	{
	  printf("( ");
	  afficheArbreExpression(arb);
	  printf(") ");
	}
      suivant_Ens(arbre->ensemble);
      if(arbre->ensemble->courant != NULL)
	printf("%s ", arbre->element);
    }
}
/*
void afficheArbre(const Arbre *arbre)
{

}
*/
char getOperateurArbre(char **expression)
{
  while((*expression)[0] == ' ')
    (*expression) ++;
  
  return (*expression)[0];
}

int getEntierArbre(char **expression)
{
  int ret;
  char *endptr = NULL;
  ret = strtol((*expression), &endptr, 10);
  if((*expression) == endptr)
    (*expression) = '\0';
  else
    (*expression) = endptr;
  return ret;
}

char estOperateur(char **expression)
{
  char *endptr = NULL;
  while((*(*expression)) == ' ')
    (*expression)++;
  (void) strtol((*expression), &endptr, 10);
  if((*expression) == endptr)
    {
      if((*expression)[0] == '*' ||
	 (*expression)[0] == '/' ||
	 (*expression)[0] == '+' ||
	 (*expression)[0] == '-' ||
	 (*expression)[0] == '(' ||
	 (*expression)[0] == ')')
	return 1;
    }
  return 0;
}

void parseArbre(Arbre **arbre, char *expression)
{
  char *lf = expression;
  Pile p;
  int j;
  TableauDynamique t;
  initialiserPile(&p);
  init(&t);
  
  if((*arbre) != NULL)
    detruireArbre(arbre);
  while((*lf) != '\0')
    { 
      if(estOperateur(&lf) == 1)
	{
	  char op = getOperateurArbre(&lf);
	  if(op == '(' || op == ')')
	    {
	      char *c;
	      if(op == '(')
		{
		  c = (char *) malloc(2*sizeof(char));
		  c[0] = op;
		  c[1] = '\0';
		  emPiler(&p, c, creerChar);
		  free(c);
		}
	      else
		{
		  while(testPileVide(&p) == 0)
		    {
		      c = sommetDeLaPile(&p);
		      if((*c) == '(')
			{
			  dePiler(&p, detruireChar);
			  break;
			}
		      ajout(&t, c, creerChar);
		      dePiler(&p, detruireChar);
		    }
		}
	    }
	  else
	    {
	      char *cop = NULL;	      
	      while(testPileVide(&p) == 0)
		{
		  char *c = sommetDeLaPile(&p);
		  if((op == '+' || op == '-') && 
		     ((*c) != '=' && (*c) != '(') )
		    {
		      ajout(&t, c, creerChar);
		      dePiler(&p, detruireChar);
		    }
		  else if((op == '*' || op == '/') &&
			  ((*c) == '*' || op == '/')
			  && ((*c) != '(' && (*c) != ')'))
		    {
		      ajout(&t, c, creerChar);
		      dePiler(&p, detruireChar);
		    }
		  else
		    break;
		}
	      cop = (char *) malloc(2*sizeof(char));
	      cop[0] = op;
	      cop[1] = '\0';
	      emPiler(&p, cop, creerChar);
	      free(cop);
	    }
	  lf++;
	}
      else
	{
	  int i = getEntierArbre(&lf);
	  ajout(&t, &i, creerEntier);
	}
    }
  
  while(testPileVide(&p) == 0)
    {
      ajout(&t, sommetDeLaPile(&p), creerChar);
      dePiler(&p, detruireChar);
    }
  testamentPile(&p, detruireChar);
  for(j = 0; j < t.utilise; j ++)
    {
      printf("%s ", (char *)t.tableau[j]);
    }
  printf("\n");
  initArbreTableau(arbre, &t);

  testament(&t, detruireChar);
}

int getEntier(char **str, int* result)
{
  if(!estOperateur(str))
    {
      (*result) = atoi((*str));
      return 1;
    }
  else
    {
      (*result) = -1;
      return 0;
    }
}

void *creerEntier(const void *i)
{
  char *ret = NULL;
  int taille = 0;
  FILE *_null = NULL;
  const int *d = i;
  _null = fopen("/dev/null", "w");
  taille = fprintf(_null, "%d", (*d));
  fclose(_null);
  
  ret = (char *) malloc ((taille + 1) *sizeof(char));
  if(ret == NULL)
    {
      fprintf(stderr, "Erreur malloc\n");
      exit(1);
    }
  sprintf(ret, "%d", (*d));
  return ret;
}

void detruireEntier(void **i)
{
  if((*i) != NULL)
    {
      free((*i));
      (*i) = NULL;
    }
}

void copierEntier(void **dest, const void *src)
{
  const int *var = src;
  detruireEntier(dest);
  (*dest) = creerEntier(var);
}

void *creerChar(const void *i)
{
  char *ret = NULL;
  const char *s = i;
  if(i == NULL)
    return ret;
  ret = (char *) malloc((strlen(s) + 1) * sizeof(char));
  if(ret == NULL)
    {
      fprintf(stderr, "Erreur malloc");
      exit(1);
    }
  sprintf(ret, "%s", s);
  return ret;
}

void detruireChar(void **i)
{
  char **d = (char **)i;
  if((*d) != NULL)
    {
      free((*d));
      (*d) = NULL;
    }
}

void copierChar(void **dest, const void *src)
{
  const char *var = src;
  detruireChar(dest);
  (*dest) = creerChar(var);
}
